Kompresja danych (1)

Od prawie początku istnienia komputerów osobistych zaistniała potrzeba zmniejszania objętości danych. Z początku stosowanie kompresji było powodowane małą ilością megabajtów na nośnikach danych - jeszcze 10 lat temu standardowe 200 MB to było tyle co dziś standardowe 60 GB. Jednakże pomimo postępu w dziedzinie nośników danych kompresja jest nadal stosowana powszechnie z dwóch powodów - po pierwsze - Internet dla przeciętnego użytkownika to transfer od kilku do kilkunastu kb/s a przesłanie 1 MB zajmuje kilkanaście minut, po drugie - kompresja wprowadza ład skupiając dużą ilość plików w jednym.
Kompresją danych nazywamy tylko te zmiany zmniejszające objętość danych zachodzące na ich cyfrowej postaci, czyli wykluczamy tutaj np. wielowarstwowość płyt DVD czy też wielomodowość światłowodów, bowiem te metody upchania i przyspieszenia przesyłu manipulują na fizycznych obiektach takich jak płyta, czy ilość wiązek lasera i jego kolorów w światłowodzie.

Na czym polega kompresja danych? Istota jej jest dosyć prosta. Dane cyfrowe składają się z jedynek i zer zapisanych po 8 w szeregu tworząc bajt. Liczbie 2 odpowiada 01, a nie 01000000 jak by to powinno mieć miejsce. Im większa liczba, tym więcej pozycji jej zapisanie zajmuje, liczby powyżej 255 potrzebują już dodatkowego bajta. Wynika z tego, że zapisując cztery dwójki:
2222
zajmą one tyle co dwieście pięćdziesiąt pięć:
255
Wysuwa się z tego rzecz następująca - najczęściej powtarzający się znak zastąpimy jedynką, która zajmuje najmniej miejsca, a resztę hierarchicznie według częstości ich występowania. W tekście polskim najczęściej występuje "a" - 11%, tuż za jest "e" - 9% itd. a w angielskim pierwsze jest "e". Kompresja wydziela te znaki analizując kawałki danych. Dzięki temu da się zaoszczędzić nawet połowę miejsca.
W takiej kompresji na początku łańcucha lub pliku zapisywana jest tabela opisująca jak znaki były zmieniane. Kompresja taka nazwana jest kompresją Huffmana.

Inna metoda kompresju - Lampel-Ziv-Welch (LZW) analizuje dane znak po znaku i zapisuje każdą kombinację (frazę) przypisując jej numer i zapisując do specjalnej tabeli. W przypadku powtórzenia się kombinacji, nie jest ona znowu zapisywana do tabeli. Jest to bardzo skomplikowana metoda i co najważniejsze przy zróżnicowanych danych nie sprawdza się, jednak np. przy grafice jest doskonała.

Do grafiki także szczególnie nadaje się Run-Leght Encoding (RLE). Jeśli znak powtórzy się więcej niż trzy razy, to jest zapisywany tylko ten znak i liczba powtórzeń. Przedstawiam poniżej schemat - przed znakiem ^ jest liczba powtórzeń. Łańcuch danych do przetwarzania:
DDDDDDBBNNNNKKKKKOOO
po skompresowaniu:
6^DBB4^N5^KOOO
Jak widać zaoszczędziliśmy około 25% miejsca. Oczywiście w rzeczywistości nie wygląda to tak, wszystko odbywa się na bitach i w rezultacie widnieją tu całkowicie inne znaki. Poniżej znajdują się funkcje do kompresji i dekompresji RLE. Odpowiednie ich zastosowanie, poprzez równoległe przetwarzanie porcjowe pozwala efektywnie i szybko "pakować" grafikę.

Public Sub RLECompress(txtDataIn As String, txtDataOut As String)
    Dim intCharCount As Integer
    Dim intNewChar As Integer
    Dim intLastChar As Integer
    Dim intCharLoop As Integer
    Dim lonCharPtr As Long
    Dim lonCharStrLen As Long
    Dim strCompressed As String
    Dim bytMgrChar As Byte
    
    intCharCount = -1
    lonCharStrLen = Len(txtDataIn)
    
    For lonCharPtr = 1 To (lonCharStrLen + 1)
    If lonCharPtr < lonCharStrLen + 1 Then
        intNewChar = Asc(Mid(txtDataIn, lonCharPtr, 1))
    Else
        intNewChar = -1
    End If
    
    intCharCount = intCharCount + 1
    If lonCharPtr > 1 Then
        If intNewChar = intLastChar Then
            If intCharCount = 128 Then
                bytMgrChar = 128
                bytMgrChar = bytMgrChar Or 127
                strCompressed = strCompressed & Chr(bytMgrChar) & Chr(intLastChar)
                intCharCount = 0
            End If
        Else
            If intCharCount > 2 Then
                bytMgrChar = 128
                bytMgrChar = bytMgrChar Or (intCharCount - 1)
                strCompressed = strCompressed & Chr(bytMgrChar) & Chr(intLastChar)
            Else
                For intCharLoop = 1 To intCharCount
                    If bytLastChar > 127 Then
                        bytOutChar = 128
                        bytOutChar = bytOutChar Or (intCharCount - 1)
                        strCompressed = strCompressed & Chr(bytMgrChar) & Chr(intLastChar)
                    Else
                        strCompressed = strCompressed & Chr(intLastChar)
                    End If
                Next intCharLoop
            End If
            
            intCharCount = 0
        End If
    End If
    intLastChar = intNewChar
    Next lonCharPtr
    
    txtDataOut = strCompressed
End Sub

Public Sub RLEUncompress(txtDataIn As String, txtDataOut As String)
    Dim bytNewChar As Byte
    Dim intCharCount As Integer
    Dim lonCharPtr As Long
    Dim strUncompressed As String
    lonCharPtr = 0
    Do
        lonCharPtr = lonCharPtr + 1
        bytNewChar = Asc(Mid(txtDataIn, lonCharPtr, 1))
        If bytNewChar > 127 Then
            intCharCount = (bytNewChar And 127) + 1
            lonCharPtr = lonCharPtr + 1
            bytNewChar = Asc(Mid(txtDataIn, lonCharPtr, 1))
            strUncompressed = strUncompressed & String(intCharCount, bytNewChar)
        Else
            strUncompressed = strUncompressed & Chr(bytNewChar)
        End If
    Loop Until (lonCharPtr >= Len(txtDataIn))

    txtDataOut = strUncompressed
End Sub


Przy tworzeniu artykułu wykorzystano materiały z książki "Visual Basic 6 - księga eksperta" Rob'a Thay'era, wyd. Helion.

Marcin Porębski ( Doogie )

marcin.porebski@interia.pl